package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


@Component
@Aspect
public class TingshuLoginAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 处理登录相关的切面方法
     * 该方法主要用于检查用户是否登录，根据方法上的注解和请求头中的token进行判断
     *
     * @param point 切入点对象，包含着当前拦截的方法的信息
     * @param tingshuLogin 自定义注解，用于标记需要登录验证的方法
     * @return Object 返回执行链路中的下一个方法的结果
     * @throws Throwable 如果用户未登录或登录信息无效，抛出异常
     */
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tingshuLogin)")
    public Object dotingshuLoginAspect(ProceedingJoinPoint point, TingshuLogin tingshuLogin) throws Throwable {
        Object object = new Object();
        //  如何判断用户是否登录的?
        /*
            需要通过token 来判断用户是否登录!
            如果请求头中有token，则说明登录！否则，没有登录！
         */
        // 获取到请求头中的 token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        // 获取到request对象
        HttpServletRequest request = servletRequestAttributes.getRequest();
        // 获取到response对象
        // HttpServletResponse response = servletRequestAttributes.getResponse();
        String token = request.getHeader("token");
        // 判断是否登录
        if (tingshuLogin.required()) {
            // 判断token是否为空,抛出异常
            if (StringUtils.isEmpty(token)) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            // 根据token 从redis中获取用户信息; user_info == UserInfo;
            String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
            if (null == userInfo) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }

        try {
            // 说明此处不需要登录
            if (!StringUtils.isEmpty(token)) {
                // 从缓存中获取数据
                String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
                if (null != userInfo) {
                    // 存储 用户Id 到本地线程
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            return point.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            // 释放本地内存，防止ThreadLocal 内存泄漏！
            AuthContextHolder.removeUserId();
        }
    }
}
